#include "dds.h"
#include "math.h"
#include <stdlib.h>
#define DAC_SAMPELS_PERPERIOD 10000
#define DAC_TIM6_MAX_FREQENCY (84000000)
uint16_t dac_data[DAC_SAMPELS_PERPERIOD];
STRDACRegType g_dac_stRegs;
void dac_init(void);
void dac_dma_init(U16 cnt){
	DMA_InitTypeDef DMA_InitStruct;
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);

	DMA_DeInit(DMA1_Stream5);  

	DMA_InitStruct.DMA_Channel = DMA_Channel_7;         
	DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&DAC->DHR12R1; 
	DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)dac_data;         
	DMA_InitStruct.DMA_DIR = DMA_DIR_MemoryToPeripheral;  
	DMA_InitStruct.DMA_BufferSize = cnt;
	DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;    
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;             
	DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; 
	DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
	DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;          
	DMA_InitStruct.DMA_Priority = DMA_Priority_High;
	DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
	DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
	DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
	DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
	DMA_Init(DMA1_Stream5, &DMA_InitStruct);
	DMA_Cmd(DMA1_Stream5, ENABLE);
	DAC_DMACmd(DAC_Channel_1, ENABLE);
}
void dac_timer_init(U16 pr, U16 arr){
	TIM_TimeBaseInitTypeDef TIM_InitStruct;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);

	TIM_InitStruct.TIM_Prescaler = pr;         
	TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_InitStruct.TIM_Period = arr;             
	TIM_InitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInit(TIM6, &TIM_InitStruct);
	TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update);
	
}

#define TIMER_MAX_FREQUENCY (SystemCoreClock /2)
#define DMA_MAX_SIZE 1000
void dac_cal_timer(U32 freq, U32 *pr, U32 *arr, U32 *dmasize){
	U32 par, parmin;
  U32 oppr2, oparr;
	U32 maxdiff, cpar, npar, curdiff, cpr2, carr, cdmasize;
	U32 i;
	par = (TIMER_MAX_FREQUENCY + freq / 2) / freq;
	cpar = par / DMA_MAX_SIZE;
	oppr2 = 0;
	oparr = 0;
	maxdiff = 1e5;
	for(i = 0; i < cpar; i++){
		cpr2 = i;
		carr = (cpar + (cpr2 + 1) / 2)/ (cpr2 + 1);
		if(carr > 0){
			carr = carr - 1;
			npar = (carr + 1) *(cpr2 + 1);
			if(npar >= cpar){
				curdiff = npar - cpar;
			}else{
				curdiff = cpar - npar;
			}
			if(curdiff < maxdiff){
				maxdiff = curdiff;
				oppr2 = cpr2;
				oparr = carr;
			}
			
		}
	}
	cpar = (oppr2 + 1) *(oparr + 1);
	
	cdmasize = (par + cpar / 2) / cpar;
	*pr = oppr2;
	*arr = oparr;
	*dmasize = cdmasize;
}

void dac_calsin(U16 *buff, U16 amp, U16 samples){
	U16 i;
	float val;
	if(amp > 2000){
		amp = 2000;
	}
	for(i = 0; i < samples; i++){
	  val = (float)amp * sin(2 * 3.1415926 / samples * i) + 2048;
		buff[i] = (U16)val;
	}
}

void dac_gpio_init(void){
	GPIO_InitTypeDef GPIO_InitStructure;			
	RCC->APB1ENR |= RCC_APB1ENR_DACEN;
	RCC->AHB1ENR |= RCC_AHB1Periph_GPIOA;

	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_InitStructure.GPIO_Pin = DAC_GPIO_PIN;
	GPIO_Init(DAC_GPIO_PORT, &GPIO_InitStructure);

	RCC->AHB1ENR |= RCC_AHB1Periph_GPIOA;
}
void dac_module_init(void){
	DAC_InitTypeDef DAC_InitStruct;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);

	DAC_InitStruct.DAC_Trigger = DAC_Trigger_T6_TRGO; 
	DAC_InitStruct.DAC_WaveGeneration = DAC_WaveGeneration_None;
	DAC_InitStruct.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
	DAC_Init(DAC_Channel_1, &DAC_InitStruct);
	DAC_Cmd(DAC_Channel_1, ENABLE);
}
void dac_init(void){
	memset(&g_dac_stRegs , 0, sizeof(g_dac_stRegs));
	dac_gpio_init();
}

void dac_startdds(U32 freq, U16 amp){
	g_dac_stRegs.freq = freq;
	g_dac_stRegs.amp = amp;
	dac_cal_timer(g_dac_stRegs.freq, &g_dac_stRegs.pr,&g_dac_stRegs.arr, &g_dac_stRegs.samples);
	dac_calsin(dac_data, (U16)g_dac_stRegs.amp, (U16)g_dac_stRegs.samples);
	
	dac_dma_init(g_dac_stRegs.samples);
	dac_module_init();
	dac_timer_init(g_dac_stRegs.pr -1 , g_dac_stRegs.arr - 1);
	TIM_Cmd(TIM6, TRUE);
	g_dac_stRegs.start = TRUE;
}
void dac_stopdds(void){
	
	TIM_Cmd(TIM6, FALSE);
	g_dac_stRegs.start = FALSE;
}

